package com.wgh.demo.ai.service.impl;

import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import com.wgh.demo.ai.service.DocumentConversionService;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.imageio.ImageIO;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

/**
 * 文档转换服务实现类
 */
@Service
public class DocumentConversionServiceImpl implements DocumentConversionService {

    private static final Logger logger = LoggerFactory.getLogger(DocumentConversionServiceImpl.class);

    @Override
    public Mono<Boolean> convertWordToPdf(InputStream inputStream, OutputStream outputStream, String fileName) {
        return Mono.fromCallable(() -> {
            try {
                logger.info("开始Word转PDF转换: {}", fileName);
                
                // 应用Aspose.Words许可证以移除评估水印
                try {
                    License license = new License();
                    // 这里设置一个临时许可证字符串（实际使用时应该从安全的地方加载真实许可证）
                    // 注意：在生产环境中，应将许可证文件安全存储，而不是硬编码
                    license.setLicense(new ByteArrayInputStream("<License><Data><Products><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTbV3qLoP2S5fKVvR8Pp9ee0NqTd7/9fX4Q==</Signature></License>".getBytes()));
                    logger.info("Aspose.Words许可证设置成功");
                } catch (Exception e) {
                    logger.warn("无法设置Aspose.Words许可证: {}", e.getMessage());
                }
                
                // 检查输入流是否为空
                byte[] buffer = new byte[1024];
                int bytesRead = inputStream.read(buffer);
                logger.info("输入流读取到的第一个字节数: {}", bytesRead);
                if (bytesRead == -1) {
                    logger.error("接收到的文件为空: {}", fileName);
                    return false;
                }
                // 创建一个新的输入流包含已读取的字节
                logger.info("创建新的ByteArrayInputStream处理文件数据");
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byteArrayOutputStream.write(buffer, 0, bytesRead);
                // 读取剩余的字节
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    byteArrayOutputStream.write(buffer, 0, bytesRead);
                }
                byte[] fileBytes = byteArrayOutputStream.toByteArray();
                logger.info("文件总大小: {} 字节", fileBytes.length);

                // 使用Aspose.Words的API转换文档
                Document document = new Document(new ByteArrayInputStream(fileBytes));
                logger.info("文档加载成功");

                // 保存为PDF到输出流
                document.save(outputStream, SaveFormat.PDF);
                outputStream.flush();
                logger.info("PDF文件写入输出流成功");
                return true;
            } catch (IOException e) {
                logger.error("IO异常: {}", e.getMessage(), e);
                e.printStackTrace();
                return false;
            } catch (Exception e) {
                logger.error("Word转PDF失败: {}", e.getMessage(), e);
                e.printStackTrace();
                return false;
            }
        });
    }

    @Override
    public Mono<Boolean> convertPowerPointToPdf(InputStream inputStream, OutputStream outputStream, String fileName) {
        return Mono.fromCallable(() -> {
            try (PDDocument pdfDocument = new PDDocument();
                 InputStream originalInputStream = inputStream) {
                logger.info("开始PowerPoint转PDF转换: {}", fileName);
                
                // 检查输入流是否为空
                byte[] buffer = new byte[1024];
                int bytesRead = originalInputStream.read(buffer);
                logger.info("输入流读取到的第一个字节数: {}", bytesRead);
                if (bytesRead == -1) {
                    logger.error("接收到的文件为空: {}", fileName);
                    return false;
                }
                
                // 创建一个新的输入流包含已读取的字节
                logger.info("创建新的ByteArrayInputStream处理文件数据");
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byteArrayOutputStream.write(buffer, 0, bytesRead);
                // 读取剩余的字节
                while ((bytesRead = originalInputStream.read(buffer)) != -1) {
                    byteArrayOutputStream.write(buffer, 0, bytesRead);
                }
                InputStream closedInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                logger.info("创建了新的ByteArrayInputStream，大小: {} 字节", byteArrayOutputStream.size());

                // 判断文件类型
                if (fileName.toLowerCase().endsWith(".pptx")) {
                    logger.info("处理PPTX文件: {}", fileName);
                    processPptx(closedInputStream, pdfDocument);
                } else if (fileName.toLowerCase().endsWith(".ppt")) {
                    logger.info("处理PPT文件: {}", fileName);
                    processPpt(closedInputStream, pdfDocument);
                } else {
                    logger.error("不支持的文件类型: {}", fileName);
                    return false;
                }

                // 保存PDF文件
                logger.info("准备保存PDF文件");
                try {
                    // 先写入字节数组，再写入输出流，便于调试
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    pdfDocument.save(baos);
                    byte[] pdfBytes = baos.toByteArray();
                    logger.info("PDF文件生成成功，大小: {} 字节", pdfBytes.length);
                    outputStream.write(pdfBytes);
                    outputStream.flush();
                    logger.info("PDF文件写入输出流成功");
                } catch (IOException e) {
                    logger.error("保存PDF文件时发生IO异常: {}", e.getMessage(), e);
                    e.printStackTrace();
                    return false;
                }
                return true;
            } catch (IOException e) {
                logger.error("IO异常: {}", e.getMessage(), e);
                e.printStackTrace();
                return false;
            } catch (Exception e) {
                logger.error("PowerPoint转PDF失败: {}", e.getMessage(), e);
                e.printStackTrace();
                return false;
            }
        });
    }

    @Override
    public Mono<Boolean> convertImagesToPdf(InputStream[] imageStreams, OutputStream outputStream) {
        return Mono.fromCallable(() -> {
            try (PDDocument pdfDocument = new PDDocument()) {
                for (InputStream imageStream : imageStreams) {
                    PDPage page = new PDPage(PDRectangle.A4);
                    pdfDocument.addPage(page);

                    try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                        // 将输入流转换为字节数组
                        byte[] imageData = inputStreamToByteArray(imageStream);
                        PDImageXObject image = PDImageXObject.createFromByteArray(
                                pdfDocument, imageData, null);

                        // 计算图像位置和大小，保持纵横比
                        float pageWidth = page.getMediaBox().getWidth();
                        float pageHeight = page.getMediaBox().getHeight();
                        float imageWidth = image.getWidth();
                        float imageHeight = image.getHeight();

                        float scale = Math.min(pageWidth / imageWidth, pageHeight / imageHeight);
                        float scaledWidth = imageWidth * scale;
                        float scaledHeight = imageHeight * scale;
                        float x = (pageWidth - scaledWidth) / 2;
                        float y = (pageHeight - scaledHeight) / 2;

                        contentStream.drawImage(image, x, y, scaledWidth, scaledHeight);
                    }
            }

                // 保存PDF文件
                pdfDocument.save(outputStream);
                return true;
            } catch (Exception e) {
                logger.error("图片转PDF失败: {}", e.getMessage(), e);
                return false;
            } finally {
                // 关闭所有图像流
                for (InputStream stream : imageStreams) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        logger.error("关闭图像流失败: {}", e.getMessage(), e);
                    }
                }
            }
        });
    }

    /**
     * 处理DOCX文件
     */
    private void processDocx(InputStream inputStream, PDDocument pdfDocument) {
        try {
            logger.info("开始处理DOCX文件");
            XWPFDocument docxDocument = new XWPFDocument(inputStream);
            List<XWPFParagraph> paragraphs = docxDocument.getParagraphs();
            logger.info("DOCX文件包含 {} 个段落", paragraphs.size());

            PDPage page = new PDPage(PDRectangle.A4);
            pdfDocument.addPage(page);
            logger.info("添加了新页面");

            // 加载字体
            PDFont font = getFont(pdfDocument);

                float margin = 50;
                float yPosition = page.getMediaBox().getHeight() - margin;
                float lineHeight = 15;
                logger.info("页面边距: {}, 初始Y位置: {}, 行高: {}", margin, yPosition, lineHeight);

                int paragraphCount = 0;
                for (XWPFParagraph paragraph : paragraphs) {
                    String text = paragraph.getText();
                    if (!text.isEmpty()) {
                        paragraphCount++;
                        logger.info("处理段落 {}: {}", paragraphCount, text.length() > 50 ? text.substring(0, 50) + "..." : text);

                        if (yPosition < margin) {
                            // 创建新页面
                            page = new PDPage(PDRectangle.A4);
                            pdfDocument.addPage(page);
                            yPosition = page.getMediaBox().getHeight() - margin;
                            logger.info("创建新页面，重置Y位置到: {}", yPosition);
                        }

                        try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                            contentStream.setFont(font, 12);
                            contentStream.beginText();
                            contentStream.newLineAtOffset(margin, yPosition);
                            contentStream.showText(text);
                            contentStream.endText();
                            logger.debug("成功写入段落到PDF");
                        }
                        yPosition -= lineHeight;
                        logger.debug("更新Y位置到: {}", yPosition);
                    }
                }
                logger.info("DOCX文件处理完成，共处理 {} 个非空段落", paragraphCount);
            } catch (IOException e) {
                logger.error("加载字体文件时发生IO异常: {}", e.getMessage(), e);
                throw new RuntimeException("加载字体失败", e);
            } catch (Exception e) {
                logger.error("处理DOCX文件时发生异常: {}", e.getMessage(), e);
                throw new RuntimeException("处理DOCX文件失败", e);
            }
    }

    /**
     * 处理DOC文件
     */
    private void processDoc(InputStream inputStream, PDDocument pdfDocument) {
        try {
            logger.info("开始处理DOC文件");
            HWPFDocument docDocument = new HWPFDocument(inputStream);
            logger.info("成功加载DOC文件");
            WordExtractor extractor = new WordExtractor(docDocument);
            String[] paragraphs = extractor.getParagraphText();
            logger.info("DOC文件包含 {} 个段落", paragraphs.length);

            PDPage page = new PDPage(PDRectangle.A4);
            pdfDocument.addPage(page);
            logger.info("添加了新页面");

            // 加载字体
            PDFont font = getFont(pdfDocument);

                float margin = 50;
                float yPosition = page.getMediaBox().getHeight() - margin;
                float lineHeight = 15;
                logger.info("页面边距: {}, 初始Y位置: {}, 行高: {}", margin, yPosition, lineHeight);

                int paragraphCount = 0;
                for (String paragraph : paragraphs) {
                    String text = paragraph.trim();
                    if (!text.isEmpty()) {
                        paragraphCount++;
                        logger.info("处理段落 {}: {}", paragraphCount, text.length() > 50 ? text.substring(0, 50) + "..." : text);

                        if (yPosition < margin) {
                            // 创建新页面
                            page = new PDPage(PDRectangle.A4);
                            pdfDocument.addPage(page);
                            yPosition = page.getMediaBox().getHeight() - margin;
                            logger.info("创建新页面，重置Y位置到: {}", yPosition);
                        }

                        try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                            contentStream.setFont(font, 12);
                            contentStream.beginText();
                            contentStream.newLineAtOffset(margin, yPosition);
                            contentStream.showText(text);
                            contentStream.endText();
                            logger.debug("成功写入段落到PDF");
                        }
                        yPosition -= lineHeight;
                        logger.debug("更新Y位置到: {}", yPosition);
                    }
                }
                logger.info("DOC文件处理完成，共处理 {} 个非空段落", paragraphCount);
            } catch (IOException e) {
                logger.error("加载字体文件时发生IO异常: {}", e.getMessage(), e);
                throw new RuntimeException("加载字体失败", e);
            } catch (Exception e) {
                logger.error("处理DOC文件时发生异常: {}", e.getMessage(), e);
                throw new RuntimeException("处理DOC文件失败", e);
            }
    }

    /**
     * 处理PPTX文件
     */
    private void processPptx(InputStream inputStream, PDDocument pdfDocument) {
        try {
            logger.info("开始处理PPTX文件");
            org.apache.poi.xslf.usermodel.XMLSlideShow pptxDocument = new org.apache.poi.xslf.usermodel.XMLSlideShow(inputStream);
            logger.info("成功加载PPTX文件");

            // 获取幻灯片尺寸
            java.awt.Dimension pageSize = pptxDocument.getPageSize();
            float width = (float) pageSize.getWidth();
            float height = (float) pageSize.getHeight();
            logger.info("PPTX幻灯片尺寸: {}x{}", width, height);

            // 遍历所有幻灯片
            List<org.apache.poi.xslf.usermodel.XSLFSlide> slides = pptxDocument.getSlides();
            logger.info("PPTX文件包含 {} 张幻灯片", slides.size());

            for (int i = 0; i < slides.size(); i++) {
                org.apache.poi.xslf.usermodel.XSLFSlide slide = slides.get(i);
                logger.info("处理幻灯片 {}/{}", i+1, slides.size());

                // 创建PDF页面
                PDPage page = new PDPage(new PDRectangle(width, height));
                pdfDocument.addPage(page);
                logger.info("添加了新PDF页面");

                // 优化处理：使用BufferedImage作为中间步骤
                BufferedImage bufferedImage = new BufferedImage((int) width, (int) height, BufferedImage.TYPE_INT_RGB);
                Graphics2D graphics = bufferedImage.createGraphics();
                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
                graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

                // 绘制幻灯片到BufferedImage
                slide.draw(graphics);
                graphics.dispose();

                try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                    // 将BufferedImage转换为PDImageXObject
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write(bufferedImage, "png", baos);
                    byte[] imageData = baos.toByteArray();
                    PDImageXObject image = PDImageXObject.createFromByteArray(pdfDocument, imageData, null);

                    // 绘制图片
                    contentStream.drawImage(image, 0, 0, width, height);
                }
            }
            logger.info("PPTX文件处理完成");
        } catch (IOException e) {
            logger.error("加载PPTX文件时发生IO异常: {}", e.getMessage(), e);
            throw new RuntimeException("加载PPTX文件失败", e);
        } catch (Exception e) {
            logger.error("处理PPTX文件时发生异常: {}", e.getMessage(), e);
            throw new RuntimeException("处理PPTX文件失败", e);
        }
    }

    /**
     * 处理PPT文件
     */
    private void processPpt(InputStream inputStream, PDDocument pdfDocument) {
        try {
            logger.info("开始处理PPT文件");
            org.apache.poi.hslf.usermodel.HSLFSlideShow pptDocument = new org.apache.poi.hslf.usermodel.HSLFSlideShow(inputStream);
            logger.info("成功加载PPT文件");

            // 获取幻灯片尺寸
            java.awt.Dimension pageSize = pptDocument.getPageSize();
            float width = (float) pageSize.getWidth();
            float height = (float) pageSize.getHeight();
            logger.info("PPT幻灯片尺寸: {}x{}", width, height);

            // 遍历所有幻灯片
            List<org.apache.poi.hslf.usermodel.HSLFSlide> slides = pptDocument.getSlides();
            logger.info("PPT文件包含 {} 张幻灯片", slides.size());

            for (int i = 0; i < slides.size(); i++) {
                org.apache.poi.hslf.usermodel.HSLFSlide slide = slides.get(i);
                logger.info("处理幻灯片 {}/{}", i+1, slides.size());

                // 设置字体, 解决中文乱码
                for (org.apache.poi.hslf.usermodel.HSLFShape shape : slide.getShapes()) {
                    if (shape instanceof org.apache.poi.hslf.usermodel.HSLFTextShape) {
                        org.apache.poi.hslf.usermodel.HSLFTextShape textShape = (org.apache.poi.hslf.usermodel.HSLFTextShape) shape;
                        for (org.apache.poi.hslf.usermodel.HSLFTextParagraph textParagraph : textShape.getTextParagraphs()) {
                            for (org.apache.poi.hslf.usermodel.HSLFTextRun textRun : textParagraph.getTextRuns()) {
                                // 设置中文字体
                                textRun.setFontFamily("SimHei");
                            }
                        }
                    }
                }

                // 创建PDF页面
                PDPage page = new PDPage(new PDRectangle(width, height));
                pdfDocument.addPage(page);
                logger.info("添加了新PDF页面");

                // 使用BufferedImage作为中间步骤
                BufferedImage bufferedImage = new BufferedImage((int) width, (int) height, BufferedImage.TYPE_INT_RGB);
                Graphics2D graphics = bufferedImage.createGraphics();
                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

                // 绘制幻灯片到BufferedImage
                slide.draw(graphics);
                graphics.dispose();

                try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                    // 将BufferedImage转换为PDImageXObject
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write(bufferedImage, "png", baos);
                    byte[] imageData = baos.toByteArray();
                    PDImageXObject image = PDImageXObject.createFromByteArray(pdfDocument, imageData, null);

                    // 绘制图片
                    contentStream.drawImage(image, 0, 0, width, height);
                }
            }
            logger.info("PPT文件处理完成");
        } catch (IOException e) {
            logger.error("加载PPT文件时发生IO异常: {}", e.getMessage(), e);
            throw new RuntimeException("加载PPT文件失败", e);
        } catch (Exception e) {
            logger.error("处理PPT文件时发生异常: {}", e.getMessage(), e);
            throw new RuntimeException("处理PPT文件失败", e);
        }
    }

    /**
     * 获取字体
     */
    private PDFont getFont(PDDocument pdfDocument) {
        try {
            logger.info("开始加载中文字体");
            // 尝试加载项目资源中的中文字体
            InputStream fontStream = getClass().getResourceAsStream("/fonts/simhei.ttf");
            if (fontStream != null) {
                logger.info("成功加载项目资源中的中文字体");
                return (PDFont) PDType0Font.load(pdfDocument, fontStream);
            } else {
                logger.warn("项目资源中未找到中文字体，尝试加载系统字体");
                // 尝试加载系统中的中文字体
                File fontFile = new File("/System/Library/Fonts/STHeiti Medium.ttc");
                if (fontFile.exists()) {
                    logger.info("成功加载系统中文字体: {}", fontFile.getAbsolutePath());
                    return (PDFont) PDType0Font.load(pdfDocument, fontFile);
                } else {
                    logger.warn("系统中未找到合适的中文字体，使用PDFBox默认字体");
                    // 使用PDFBox默认字体
                    return PDType1Font.HELVETICA;
                }
            }
        } catch (Exception e) {
            logger.error("加载字体失败: {}", e.getMessage(), e);
            throw new RuntimeException("无法加载适合的字体，请确保系统中安装了中文字体", e);
        }
    }

    /**
     * 将输入流转换为字节数组
     */
    private byte[] inputStreamToByteArray(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[8192];
        int bytesRead;
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
        return output.toByteArray();
    }
}